package logica.locacao;

import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

import util.Constantes;
import logica.util.GeradorDeCodigo;
import logica.veiculo.Acessorio;

/**
 * Esse objeto contem informacoes que descrevem um plano de locacao, como o tipo
 * de veiculos que ele oferece, os acessorios, potencia, seguro, entre outros.
* @author Equipe Localisa (Usa quem precisa!)
*/

public class PlanoDeLocacao implements Serializable{

	private transient static final long serialVersionUID = 1L;
	
	/**
	 * Enumeracao que corresponde ao tipo de veiculo.
	 * @author Equipe Localisa (Usa quem precisa!)
	 */
	public enum TipoDeVeiculo{
		AUTOMOVEL, MOTOCICLETA;
	}
	
	private int codigo = GeradorDeCodigo.getCodigo();
	private HashMap<String, HashSet<String>> marcas;
	private Acessorio[] acessorios;
	private Seguro seguro;
	private int potencia;
	private TipoDeVeiculo tipoDeVeiculo;
	private int cilindrada;
	private double precoBaseDaDiaria;

	public PlanoDeLocacao() throws Exception{
		
		this.precoBaseDaDiaria = 0;
		this.tipoDeVeiculo = null;
		this.marcas = null;
		this.acessorios = null;
		this.potencia = 0;
		this.codigo = GeradorDeCodigo.getCodigo();
		this.seguro = null;
		this.cilindrada = 0;
		
	}

	/**
	 * Pega o preco base da diaria deste plano.
	 * @return O preco base da diaria
	 */
	public double getPrecoBaseDaDiaria(){
		return this.precoBaseDaDiaria;
	}
	
	/**
	 * Determina o preco base da diaria deste plano.
	 * @param O preco base da diaria
	 */
	public void setPrecoBaseDaDiaria(double precoBase) throws Exception{
		if ( precoBase <= 0 ){
			throw new Exception("\n \t <Preco base da diaria invalido!>");
		}
		this.precoBaseDaDiaria = precoBase;
	}
	
	/**
	 * Retorna o tipo de veiculos que esse plano oferece. 
	 * @return o tipo de veiculos
	 */
	public TipoDeVeiculo getTipoDeVeiculo(){
		return this.tipoDeVeiculo;
	}
	
	/**
	 * Determina o tipo de veiculos que esse plano oferece. 
	 * @param o tipo de veiculos
	 */
	public void setTipoDeVeiculo(TipoDeVeiculo tipo) throws Exception{
		if ( tipo == null ){
			throw new Exception("\n \t <Tipo de veiculo invalido!>");
		}
		this.tipoDeVeiculo = tipo;
	}
	
	/**
	 * Pega as cilindradas que os veiculos do plano devem ter.
	 * @return as cilindradas do veiculo
	 */
	public int getCilindradas(){
		return this.cilindrada;
	}
	
	/**
	 * Determina nova cilindrada oferecida pelo plano.
	 * @param O valor de cilindradas oferecido pelo plano
	 * @throws Exception Caso o valor de cilindradas seja negativo
	 */
	public void setCilindradas(int cilindradas) throws Exception{
		if ( cilindradas <= 0 ){
			throw new Exception("\n \t <Cilindrada invalida!>");
		}
		this.cilindrada = cilindradas;
	}
	
	/**
	 * Retorna o codigo desse plano.
	 * @return O codigo desse plano
	 */
	public int getCodigo(){
		return this.codigo;
	}
	
	/**
	 * Determina o conjunto de marcas deste plano de locacao.
	 * @return O conjunto de marcas deste plano
	 */
	public Set<String> getMarcas(){
		return this.marcas.keySet();
	}

	/**
	 * Pega o conjunto de modelos deste plano de locacao.
	 * @return O conjunto de modelos deste plano
	 */
	public Set<String> getModelos(){
		HashSet<String> retorno = new HashSet<String>();
		for ( Set<String> conjunto : this.marcas.values() ){
			for ( String elemento : conjunto ){
				retorno.add(elemento);
			}
		}
		return retorno;
	}
	
	/**
	 * Pega o mapa de marcas e modelos deste plano de locacao.
	 * @return O mapa de marcas e modelos
	 */
	public HashMap<String, HashSet<String>> getMarcaXModelos(){
		return this.marcas;
	}

	/**
	 * Determina novo mapa de mapas e modelos deste plano de locacao.
	 * @param mapaDeMarcas O mapa de marcas e modelos que este plano oferece
	 * @throws Exception Excecao caso o mapa passado como parametro seja nulo
	 */
	public void setMarcasXModelos(HashMap<String, HashSet<String>> mapaDeMarcas) throws Exception{
		if ( mapaDeMarcas == null ){
			throw new Exception("\n \t <Mapa de marcas e modelos invalido!>");
		}
		
		HashSet<String> marcas = new HashSet<String>(mapaDeMarcas.keySet());
		HashSet<String> modelos = new HashSet<String>();
		for ( HashSet<String> conjunto : mapaDeMarcas.values() ){
			for ( String elemento : conjunto ){
				modelos.add(elemento);
			}
		}
		
		for ( String marca : marcas ){
			for ( String modelo : modelos ){
				if ( marca.equals(modelo) ){
					throw new Exception("\n \t <Nao pode haver de forma alguma marcas e modelos iguais!>");
				}
			}
		}
		
		this.marcas = mapaDeMarcas;
	}
	
	/**
	 * Retorna um array de acessorios oferecidos pelo plano.
	 * @return Um array de acessorios
	 */
	public Acessorio[] getAcessorios(){
		return this.acessorios;
	}
	
	/**
	 * Determina um novo array de acessorios oferecidos pelo plano.
	 * @param acessorios Um novo array de acessorios
	 * @throws Exception Excecao de parametros invalidos
	 */
	public void setAcessorios(Acessorio[] acessorios) throws Exception{
		if ( acessorios == null || acessorios.length > Constantes.QTDE_ACESSORIOS_POR_PLANO ){
			throw new Exception("\n \t <Lista de acessorios invalida ou com limite excedido! (limite de: "+Constantes.QTDE_ACESSORIOS_POR_PLANO+" acessorios por plano)!>");
		}
		this.acessorios = acessorios;
	}
	
	/**
	 * Retorna um seguro, dentre os disponiveis.
	 * @return o seguro deste plano de locacao
	 */
	public Seguro getSeguro(){
		return this.seguro;
	}
	
	/**
	 * Determina um novo seguro para o plano.
	 * @param seguro Um novo seguro para o plano
	 */
	public void setSeguro(Seguro seguro){
		this.seguro = seguro;
	}
	
	/**
	 * Remove o seguro do plano.
	 */
	public void removerSeguro(){
		this.seguro = null;
	}
	
	/**
	 * Retorna qual a potencia os veiculos que o plano oferece devem ter.
	 * @return a potencia dos seguros que o plano oferece
	 */
	public int getPotencia(){
		return this.potencia;
	}
	
	/**
	 * Determina uma nova potencia que os veiculos que o plano oferece devem ter.
	 */
	public void setPotencia(int potencia) throws Exception{
		if ( potencia <= 0 ){
			throw new Exception("\n \t <Potencia invalida!>");
		}
		this.potencia = potencia;
	}

	/**
	 * Mostra uma representacao do objeto em String, revelando alguns
	 * de seus atributos
	 * @return Uma representacao em String do objeto PlanoDeLocacao
	 */
	@Override
	public String toString(){
		String format = "";
		format += " \t ______________________________________________________________\n";
		format += "\n \t Plano de Locacao "+this.getCodigo()+" de "+this.getTipoDeVeiculo();
		format += String.format("\n \t Preco base da diaria R$ %.2f",this.getPrecoBaseDaDiaria());
		if ( this.getSeguro() == null ){
			format += " \t Sem seguro de locacao ";
		}
		else{
			format += " \t Seguro "+this.getSeguro();
		}
		format+="\n \t Potencia: "+this.getPotencia();
		if ( this.getTipoDeVeiculo() == TipoDeVeiculo.MOTOCICLETA ){
			format += " \t Cilindradas: "+this.getCilindradas();
		}
		format+="\n \t MarcasXModelos: "+this.getMarcaXModelos();
		format+="\n \t Acessorios: ";
		for ( int i=0; i<this.getAcessorios().length; i++ ){
			if ( i%3 == 0 ){
				if ( this.getAcessorios()[i] != null ){
					format+="\n \t  "+this.getAcessorios()[i].getDescricao();
				}
			}
			else{
				if ( this.getAcessorios()[i] != null ){
					format+=" + "+this.getAcessorios()[i].getDescricao();
				}
			}
		}
		format += "\n \t ______________________________________________________________\n";
		return format;
	}
}